This invention relates generally to a computer method and system of implementing interfaces to objects and, more specifically, to a method and system for aggregating objects.
As computer software systems increase in sophistication, the cost of developing the software increases. To minimize the cost of software development, developers often share code. Prior development efforts use three types of code sharing: (1) source code sharing, (2) compiled code sharing, and (3) code sharing through inheritance.
Also, as computer software systems increase in sophistication, software users are faced with increasing complexity in the maintenance and extensibility of their computer systems. Each time a software vendor generates new or improved capabilities, a user who wishes to benefit must somehow incorporate these modifications. Prior systems generally require such a user to upgrade the software, forcing the user to reinstall at least part of the system. Or, prior systems require a software developer to plan in advance for future enhancements and install the necessary hooks in the original system to enable the loading of enhancements at some future time.
Moreover, if a user wishes to enhance the capabilities of a piece of currently owned software by adding capabilities produced by a different software vendor, the user is limited by what the software vendors planned in advance. Prior systems generally require that the two pieces of software be designed to work together and that at least one of the software pieces have knowledge of what capabilities the other provides. Thus, in prior systems, later modifications to code as well as the sharing of code must be accounted for in the software design.
Source and compiled code sharing have been widely used for many years. Source code sharing refers to the use of the same source code by various computer programs or by various versions of the same computer program. For example, a spreadsheet program typically includes source code to control the displaying of a spreadsheet. If a word processing program allows the embedding of a spreadsheet within a document, then the word processing program may use the same (or slightly modified) source code to display the embedded spreadsheet object. Source code sharing is typically used by a single developer who develops multiple computer programs. For competitive reasons. developers typically do not share their source code with other developers. Moreover, even if the developer does share source code, the recipient of source code typically modifies the source code and thus two versions of the source code are maintained.
Compiled code sharing refers to the use of the same compiled code by various computer programs. The compiled code is typically stored in a static or dynamic link library. Compiled code stored in a static link library is shared when a computer program is linked before execution. Compiled code stored in a dynamic link library is shared when a computer program is linked during execution. The developer of a spell checking program, for example, may share compiled code by compiling the program and storing the compiled code in a static link library. The static link library can then be distributed to developers of word processing programs who can link the compiled spell checking code into their word processing program. The developer of the spell checking program typically needs to modify the compiled code to meet special requirements of certain developers. These modifications tend to increase the complexity (and size) of the compiled code and may conflict with requirements of other recipients. Alternatively, the developer could distribute multiple versions of the static link library. However, the maintenance of multiple versions can be costly.
Object-oriented programming techniques employ a concept referred to as inheritance to allow the sharing of code. An overview of well-known object-oriented programming techniques is provided, since the present invention is described below using object-oriented programming. Two common characteristics of object-oriented programming languages are support for data encapsulation and data type inheritance. Data encapsulation refers to the binding of functions and data. Inheritance refers to the ability to declare a data type in terms of other data types.
In the C++ language, object-oriented techniques are supported through the use of classes. A class is a user-defined type. A class declaration describes the data members and function members of the class. For example, the following declaration defines data members and a function member of a class named CIRCLE.
Variables x and y specify the center location of a circle and variable radius specifies the radius of the circle. These variables are referred to as data members of the class CIRCLE. The function draw is a user-defined function that draws the circle of the specified radius at the specified location. The function draw is referred to as a function member of class CIRCLE. The data members and function members of a class are bound together in that the function operates on an instance of the class. An instance of a class is also called an object of the class.
In the syntax of C++ the following statement declares the objects a and b to be of type class CIRCLE.
CIRCLE a, b;
This declaration causes the allocation of memory for the objects a and b. The following statements assign data to the data members of objects a and b.
a.x=2;
a.y=2;
a.radius=1;
b.x=4;
b.y=5;
b.radius=2;
The following statements are used to draw the circles defined by objects a and b.
a.draw();
b.draw();
A derived class is a class that inherits the characteristicsxe2x80x94data members and function membersxe2x80x94of its base classes. For example, the following derived class CIRCLE_FILL inherits the characteristics of the base class CIRCLE.
This declaration specifies that class CIRCLE_FILL includes all the data and function members that are in class CIRCLE in addition to those data and function members introduced in the declaration of class CIRCLE FILL, that is, data member pattern and function member fill. In this example, class CIRCLE FILL has data members x, y, radius, and pattern and function members draw and fill. Class CIRCLE_FILL is said to xe2x80x9cinheritxe2x80x9d the characteristics of class CIRCLE. A class that inherits the characteristics of another class is a derived class (e.g., CIRCLE FILL). A class that does not inherit the characteristics of another class is a primary (root) class (e.g., CIRCLE). A class whose characteristics are inherited by another class is a base class (e.g., CIRCLE is a base class of CIRCLE13 FILL). A derived class may inherit the characteristics of several classes, that is, a derived class may have several base classes. This is referred to as multiple inheritance.
A derived class may specify that a base class is to be inherited virtually. Virtual inheritance of a base class means that only one instance of the virtual base class exists in the derived class. For example, the following is an example of a derived class with two nonvirtual base classes.
In this declaration class PATTERN inherits class CIRCLE twice nonvirtually through classes CIRCLExe2x80x941 and CIRCLExe2x80x942. There are two instances of class CIRCLE in class PATTERN.
The following is an example of a derived class with two virtual base classes.
The derived class PATTERN inherits class CIRCLE twice virtually through classes CIRCLExe2x80x941 and CIRCLExe2x80x942. Since the class CIRCLE is virtually inherited twice, there is only one object of class CIRCLE in the derived class PATTERN. One skilled in the art would appreciate virtual inheritance can be very useful when the class derivation is more complex.
A class may also specify whether its function members are virtual Declaring that a function member is virtual means that the function can be overridden by a function of the same name and type in a derived class. In the following example, the function draw is declared to be virtual in classes CIRCLE and CIRCLE_FILL.
If a virtual function is declared without providing an implementation, then it is referred to as a pure virtual function. A pure virtual function is a virtual function declared with the pure specifier, xe2x80x9c=0xe2x80x9d. If a class specifies a pure virtual function, then any derived class needs to specify an implementation for that function member before that function member may be invoked.
In order to access objects, the C++ language provides a pointer data type. A pointer holds values that are addresses of objects in memory. Through a pointer, an object can be referenced. The following statement declares variable c_ptr to be a pointer on an object of type class CIRCLE and sets variable c_ptr to hold the address of object c.
CIRCLE *c_ptr;
c_ptr=andc;
Continuing with the example, the following statement declares object a to be of type class CIRCLE and object b to be of type class CIRCLE_FILL.
CIRCLE a;
CIRCLE_FILL b;
The following statement refers to the function draw as defined in class CIRCLE.
a.draw();
Whereas, the following statement refers to the function draw defined in class CIRCLE_FILL.
b.draw();
Moreover the following statements type cast object b to an object of type class CIRCLE and invoke the function draw that is defined in class CIRCLE_FILL.
Thus, the virtual function that is called is function CIRCLE_FILL::draw.
FIG. 1 is a block diagram illustrating typical data structures used to represent an object. An object is composed of instance data (data members) and member functions, which implement the behavior of the object. The data structures used to represent an object comprise instance data structure 101, virtual function table 102, and the function members 103, 104, 105. The instance data structure 101 contains a pointer to the virtual function table 102 and contains data members. The virtual function table 102 contains an entry for each virtual function member defined for the object. Each entry contains a reference to the code that implements the corresponding function member. The layout of this sample object conforms to the model defined in U.S. patent application Ser. No. 07/682,537, entitled xe2x80x9cA Method for Implementing Virtual Functions and Virtual Bases in a Compiler for an Object Oriented Programming Language,xe2x80x9d which is hereby incorporated by reference. In the following, an object will be described as an instance of a class as defined by the C++ programming language. One skilled in the art would appreciate that objects can be defined using other programming languages.
The inheritance of a class is a type of code sharing. A developer of a class can provide the implementation of the class to other developers. These other developers can then create classes that derive from the class provided. Thus, the function members of the provided class are shared. If, however, a class is inherited and a virtual function is overridden, then the testing of the overriding virtual function can be complex. The overriding virtual function can modify the state of the object in a way that affects non-overridden functions. Thus, each inherited function must be independently tested in conjunction with the testing of the overriding virtual function. To ameliorate the complexities of testing, the developers of a class implementation may distribute source code with the implementation. Unfortunately, the distribution of source code has the same drawbacks to sharing source code as discussed above.
An advantage of using object-oriented techniques is that these techniques can be used to facilitate the sharing of objects. In particular, object-oriented techniques facilitate the creation of compound documents. A compound document is a document that contains objects generated by various computer programs. (Typically, only the data members of the object and the class type are stored in a compound document.) For example, a word processing document that contains a spreadsheet object generated by a spreadsheet program is a compound document. A word processing program allows a user to embed a spreadsheet object (e.g., a cell) within a word processing document. To allow this embedding, the word processing program is compiled using the class definition of the object to be embedded to access function members of the embedded object. Thus, the word processing program would need to be compiled using the class definition of each class of objects that can be embedded in a word processing document. To embed an object of a new class into a word processing document, the word processing program would need to be recompiled with the new class definition. Thus, only objects of classes selected by the developer of the word processing program can be embedded. Furthermore, new classes can only be supported with a new release of the word processing program.
To allow objects of an arbitrary class to be embedded into compound documents, interfaces are defined through which an object can be accessed without the need for the word processing program to have access to the class definitions at compile time. An abstract class is a class in which there is at least one virtual function member with no implementation (a pure virtual function member). An interface is an abstract class with no data members and whose vital functions are all pure. Thus, an interface provides a protocol for two programs to communicate. Interfaces are typically used for derivation: a program implements classes that provide implementations for the interfaces the classes are derived from. Thereafter, objects are created as instances of these derived classes.
The following class definition is an example definition of an interface. In this example, for simplicity of explanation, rather than allowing any class of object to be embedded in its documents, a word processing program allows spreadsheet objects to be embedded. Any spreadsheet object that provides this interface can be embedded, regardless of how the object is implemented. Moreover, any spreadsheet object, whether implemented before or after the word processing program is compiled can be embedded.
The developer of a spreadsheet program would need to provide an implementation of the interface to allow the spreadsheet objects to be embedded in a word processing document.
When the word processing program embeds a spreadsheet object, the program needs access to the code that implements the interface for the spreadsheet object. To access the class code, each implementation is given a unique class identifier. For example, code implementing a spreadsheet object developed by Microsoft Corporation may have a class identifier of xe2x80x9cMSSpreadsheet,xe2x80x9d while code implementing a spreadsheet object developed by another corporation may have a class identifier of xe2x80x9cLTSSpreadsheet.xe2x80x9d A persistent registry in each computer system is maintained that maps each class identifier to the code that implements the class. Typically, when a spreadsheet program is installed on a computer system, the persistent registry is updated to reflect the availability of that class of spreadsheet objects. So long as a spreadsheet developer implements each function member defined by the interface and the persistent registry is maintained, the word processing program can embed instances of the developer""s spreadsheet objects into a word processing document. The word processing program accesses the function members of the embedded spreadsheet objects without regard to who has implemented them or how they have been implemented.
Various spreadsheet developers may wish, however, to implement only certain function members. For example, a spreadsheet developer may not want to implement database support, but may want to support all other function members. To allow a spreadsheet developer to support only some of the function members, while still allowing the objects to be embedded, multiple interfaces for spreadsheet objects are defined. For example, the interfaces IDatabase and IBasic may be defined for a spreadsheet object as follows.
Each spreadsheet developer would implement the IBasic interface and, optionally, the IDatabase interface.
At run time, the word processing program would need to determine whether a spreadsheet object to be embedded supports the IDatabase interface. To make this determination, another interface is defined (that every spreadsheet object implements) with a function member that indicates which interfaces are implemented for the object. This interface is named IUnknown (and referred to as the unknown interface or the object management interface) and is defined as follows.
The IUnknown interface defines the function member (method) QueryInterface. The method QueryInterface is passed an interface identifier (e.g., xe2x80x9cIDatabasexe2x80x9d in parameter iid (of type REFIID) and returns a pointer to the implementation of the identified interface for the object for which the method is invoked in parameter ppv. If the object does not support the interface, then the method returns a false. The type HRESULT indicates a predefined status, the type REFIID indicates a reference to an interface identifier, and the type ULONG indicates an unsigned long integer.
Code Table 1 contains C++ pseudocode for a typical implementation of the method QueryInterface for class XX, which inherits the class IUnknown. If the spreadsheet object supports the IDatabase interface, then the method QueryInterface includes the appropriate case label within the switch statement. The variables pIBasic and pIDatabase point to a pointer to the virtual function tables of the IBasic and IDatabase interfaces, respectively. The method QueryInterface invokes to method AddRef (described below) to increment a reference count for the object of class XX when a pointer to an interface is returned.
The interface IUnknown also defines the methods AddRef and Release, which are used to implement reference counting. Whenever a new reference to an interface is created, the method AddRef is invoked to increment a reference count of the object. Whenever a reference is no longer needed, the method Release is invoked to decrement the reference count of the object and, when the reference count goes to zero, to deallocate the object. Code Table 2 contains C++ pseudocode for a typical implementation of the methods AddRef and Release for class XX, which inherits the class IUnknown.
The IDatabase interface and IBasic interface inherit the IUnknown interface. The following definitions illustrate the use of the IUnknown interface.
FIG. 2 is a block diagram illustrating a sample data structure of a spreadsheet object using nested classes. The spreadsheet object comprises object data structure 201, IBasic interface data structure 203, Database interface data structure 204, the virtual function tables 202, 205, 206 and methods 207 through 221. The object data structure 201 contains a pointer to the virtual function table 202 and pointers to the IBasic and IDatabase interface. Each entry in the virtual function table 202 contains a pointer to a method of the IUnknown interface. The IBasic interface data structure 203 contains a pointer to the virtual function table 205. Each entry in the virtual function table 205 contains a pointer to a method of the IBasic interface. The IDatabase interface data structure 204 contains a pointer to the virtual function table 206. Each entry in the virtual function table 207 contains a pointer to a method of the IDatabase interface. Since the IBasic and IDatabase interfaces inherit the IUnknown interface, each virtual function table 205 and 206 contains a pointer to the methods QueryInterface, AddRef, and Release. In the following, an object data structure is represented by the shape 222 labeled with the interfaces through which the object may be accessed.
The following pseudocode illustrates how a word processing program determines whether a spreadsheet object supports the IDatabase interface.
The pointer pSpreadsheet is a pointer to an instance of the spreadsheet class shown in FIG. 2. (pSpreadsheet points to data structure 201.) If the object supports the IDatabase interface, the method QueryInterface defined by method 207 sets the pointer pIDatabase to point to the IDatabase data structure 204 and returns true as its value.
Normally, an object can be instantiated (an instance of the object created in memory) by a variable declaration or by the xe2x80x9cnewxe2x80x9d operator. However, both techniques of instantiation need the class definition at compile time. A different technique is needed to allow a word processing program to instantiate a spreadsheet object at run time. One technique provides a global function CreateInstanceXX, which is defined in the following. static void CreateInstanceXX (REFIID iid, void **ppv)=0;
The method CreateInstanceXX instantiates an object of class XX and returns a pointer ppv to the interface of the object designated by parameter iid.
It is a goal of the present invention to provide a method and system for aggregating objects.
It is another goal of the present invention to provide a method and system for dynamically modifying object behavior.
It is another goal of the present invention to provide a method and system for dynamically aggregating objects.
It is another goal of the present invention to provide a method and system for statically aggregating objects.
It is another goal of the present invention to provide a method and system for enclosing an object within another object while exposing an interface of the enclosed object to a client of the enclosing object.
It is another goal of the present invention to provide a method and system for enclosing an object within another object after the enclosing object is instantiated.
It is another goal of the present invention to provide a method and system for dynamically combining objects of different types into a single object.
It is another goal of the present invention to provide a method and system for implementing an object that can be either enclosed within another object or not enclosed within another object without modifying the implementation of the object.
It is another goal of the present invention to provide a method and system for implementing an aggregate object so that a client is unaware that the object is an aggregate.
It is another goal of the present invention to provide a method and system for enclosing objects wherein an enclosed object can itself be an enclosing object to an arbitrary level of enclosing.
It is another goal of the present invention to provide a method and system for enhancing a base object""s behavior by adding a new interface to it.
It is another goal of the present invention to provide a method and system for enhancing a base object""s apparent behavior by adding an interface to it that overrides standard behavior of the base object.
It is another goal of the present invention to provide a method and system for supplying default functionality to objects by enclosing them within an enclosing object where an enclosed or enclosing object implements the default functionality.
It is another goal of the present invention to provide a method and system for implementing controlling behavior over common functionality present in enclosed objects.
It is another goal of the present invention to provide a method and system for determining which interface to provide to a client when the client requests an interface that is implemented by more than one enclosed object.
These and other goals, which will be apparent as the invention is more fully described below, are provided by a method and system for aggregating objects within a computer system. In a preferred embodiment, the method aggregates an enclosed object within an enclosing object. The enclosed object has an object management interface and one or more external interfaces, while the enclosing object has a controlling object management interface. Each interface exposed to a client by the aggregate object has a query function member for receiving an identifier of an interface and for returning a reference to the identified interface. The query function member of the controlling object management interface of the enclosing object receives an identifier of an interface exposed by the enclosing object and returns a reference to the exposed interface. A preferred method creates an instance of the enclosed object. When, the query function member of an exposed interface of the enclosed object receives an identifier of an interface, it invokes the query function member of the controlling object management interface of the enclosing object passing the received identifier, and returns the reference returned by the invoked query function member of the controlling object management interface of the enclosing object as a reference to the identified interface.
In a preferred embodiment of static aggregation, a query function member of an enclosed object is implemented with knowledge of the external interfaces of the enclosed object and has no knowledge of interfaces (other than the controlling object management interface) of the enclosing object or other enclosed objects. The query function member of a controlling object management interface of the enclosing object is implemented with knowledge of the exposed interfaces of enclosed objects.
In a preferred embodiment of dynamic aggregation, an object can be modified dynamically by allowing interface instances, as implemented by objects, to be aggregated during the execution of a client program. Interfaces are aggregated by dynamically enclosing the objects that implement them into a multitype object. Each interface to be added is implemented by an object which has the ability to be aggregated. A multitype object is created to act as an enclosing object. The multitype object has an add interface function member, which can be used to aggregate interfaces by adding them to the enclosing multitype object. The multitype object also has an add object function member for aggregating all of the interfaces of an object. The multitype object also has a query function member for retrieving references to the added interfaces upon request from a client. This query function member is part of the controlling object management interface of the enclosing multitype object. Also, an instance of an object that implements the interface to be aggregated is created. During creation, a pointer to the enclosing multitype object is passed to the object to be enclosed to enable the enclosed object to communicate with the enclosing multitype object. The created object implementing the interface to be aggregated has a query function member, which supports retrieval of a reference to the interface to be aggregated. A preferred method invokes the add interface function member or the add object function member of the enclosing multitype object passing it a reference to the created object implementing the interface to be aggregated. Later, the query function member of the enclosing multitype object is invoked in order to retrieve a reference to the interface that has been aggregated.